#include "stdafx.h"
#include "CodeGeneratorManager.h"
#include "RowNames.h"
#include "CharacterTable.h"
#include "CharData.h"

CCodeGeneratorManager::CCodeGeneratorManager(CScanner & scanner, CCodeGenerator & codeGenerator)
	:m_scanner(scanner), m_codeGenerator(codeGenerator)
{
	m_isSubProgram = false;
}

CCodeGeneratorManager::~CCodeGeneratorManager(){}

void CCodeGeneratorManager::SetChecker(CCharacterTableChecker * checker)
{
	m_checker = checker;
}

void CCodeGeneratorManager::SetCurrRowName(std::string const & currRowName)
{
	if (currRowName == RowNames::STMT_24_7)
		m_codeGenerator.IfElseStmtFinished();

	if (currRowName == RowNames::ELSE_24_6)
	{
		m_codeGenerator.AddStmtsEndLabel();
		m_codeGenerator.AddElseStmtLabel();
	}

	if (currRowName == RowNames::R_ROUND_BRACKET_24_4 || currRowName == RowNames::R_ROUND_BRACKET_25_4)
		m_codeGenerator.AddJmpForIfElseStmt();
	
	if (currRowName == RowNames::WHILE_25_1)
		m_codeGenerator.AddWhileLabel();
	
	if (currRowName == RowNames::STMT_25_5)
	{
		m_codeGenerator.AddJmpOnWhileLabel();
		m_codeGenerator.AddElseStmtLabel();
	}

	if (currRowName == RowNames::CONSTRUCTOR_6_6)
	{
		m_codeGenerator.SetIsSubProgram(true);
		ConstructorBegin();
	}
	
	if (currRowName == RowNames::R_FIGURE_BRACKET_6_12)
	{
		ConstructorEnd();
		m_codeGenerator.SetIsSubProgram(false);
		m_params.clear();
	}

	if (currRowName == RowNames::R_ROUND_BRACKET_12_5)
		FunctionBegin();

	if (currRowName == RowNames::FUNCTION_12_1)
		m_codeGenerator.SetIsSubProgram(true);

	if (currRowName == RowNames::R_FIGURE_BRACKET_12_15)
	{
		FunctionEnd();
		m_codeGenerator.SetIsSubProgram(false);
		m_params.clear();
		m_localVars.clear();
	}

	if (currRowName == RowNames::RETURN_12_12)
	{
		m_codeGenerator.MoveResultInOperand2(CheckVar(m_scanner.GetValue()));
	}
}

void CCodeGeneratorManager::R9()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	if (lastTokens[lastTokens.size() - 4] == "Int")
	{
		if (m_objects.empty() && m_checker->GetCurrClass() == "main")
		{
			std::string id = lastTokens[lastTokens.size() - 3];
			m_codeGenerator.AddIntVariable(id);
		}
		else
		{
			std::string className = m_checker->GetCurrClass();
			std::string methodName = m_checker->GetCurrMethod();
			std::map<std::string, std::string>::iterator it;

			for (it = m_objects.begin(); it != m_objects.end(); ++it)
			{
				if (className == it->second)
				{
					std::string id;
					if (methodName != "")
					{
						m_localVars.push_back(lastTokens[lastTokens.size() - 3]);
						//m_codeGenerator.AddIntLocalVariable("_l" + lastTokens[lastTokens.size() - 3]);
						m_codeGenerator.AddIntLocalVariable(lastTokens[lastTokens.size() - 3]);
						break;
					}
					else
					{
						id = it->first + "_" + lastTokens[lastTokens.size() - 3];
					}
					m_codeGenerator.AddIntVariable(id);
				}
			}
			if (methodName == "")
				m_codeGenerator.AddIntVariable(lastTokens[lastTokens.size() - 3]);
		}
	}
}

void CCodeGeneratorManager::R26()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	std::string value;
	unsigned s = lastTokens.size();
	if (lastTokens[s - 5] == "(" && lastTokens[s - 4] != ")" && lastTokens[s - 3] == ")")
	{
		value = lastTokens[s - 4];
	}
	m_codeGenerator.GeneratePrintCommand(CheckVar(value));
}

void CCodeGeneratorManager::R28()
{
	std::vector<std::string> & lastTokents = m_scanner.GetLastTokens();
	std::string id;
	for (unsigned i = lastTokents.size() - 1; i >= 0; --i)
	{
		if (lastTokents[i] == "=")
		{
			id = lastTokents[i - 1];
			break;
		}
	}
	m_codeGenerator.AddAssignmentCommand(CheckVar(id));
}

void CCodeGeneratorManager::R33()
{
	std::string op = m_operations.top();
	m_operations.pop();
	if (op == "||")
	{
		m_codeGenerator.AddLogOrOperation();
	}
	else if (op == "<")
	{
		m_codeGenerator.AddLogOperationLess();
	}
	else if (op == "<=")
	{
		m_codeGenerator.AddLogOperationLessOrEqual();
	}
	else
	{
		stringstream errorMsg;
		errorMsg <<  "Error on line " << m_scanner.GetCurrLineIndex() << "Must be a '<' or '<=' or '||' operation";

		throw std::logic_error(errorMsg.str());
	}
}

void CCodeGeneratorManager::R34()
{
	m_codeGenerator.MoveResultInOperand1();
}

void CCodeGeneratorManager::R35()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	m_operations.push(lastTokens[lastTokens.size() - 2]);
}

void CCodeGeneratorManager::R36()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	m_operations.push(lastTokens[lastTokens.size() - 2]);
}

void CCodeGeneratorManager::R37()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	m_operations.push(lastTokens[lastTokens.size() - 2]);
}

void CCodeGeneratorManager::R38()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	m_codeGenerator.AddLogAndOperation(lastTokens[lastTokens.size() - 2]);
}

void CCodeGeneratorManager::R39()
{
	m_codeGenerator.SaveIdOrIntLiteral(CheckVar(m_scanner.GetValue()));
}

void CCodeGeneratorManager::R40()
{
	std::string op = m_operations.top();
	m_operations.pop();
	if (op == "+")
	{
		m_codeGenerator.GenerateAdditionCommand();
	}
	else if (op == "-")
	{
		m_codeGenerator.GenerateSubtractionCommand();
	}
	else
	{
		stringstream errorMsg;
		errorMsg <<  "Error on line " << m_scanner.GetCurrLineIndex() << "Must be an addition or subtraction operation";

		throw std::logic_error(errorMsg.str());
	}
}

void CCodeGeneratorManager::R42()
{
	m_codeGenerator.Invert();
}

void CCodeGeneratorManager::R43()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	m_operations.push(lastTokens[lastTokens.size() - 2]);
}

void CCodeGeneratorManager::R44()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	m_operations.push(lastTokens[lastTokens.size() - 2]);
}

void CCodeGeneratorManager::R45()
{
	std::string op = m_operations.top();
	m_operations.pop();
	if (op == "*")
	{
		m_codeGenerator.GenerateMultiplicationCommand();
	}
	else if (op == "/")
	{
		m_codeGenerator.GenerateDivisionCommand();
	}
	else
	{
		stringstream errorMsg;
		errorMsg <<  "Error on line " << m_scanner.GetCurrLineIndex() << "Must be a multiplication or division operation";

		throw std::logic_error(errorMsg.str());
	}
}

void CCodeGeneratorManager::R47()
{
	m_codeGenerator.MoveToRegister(m_scanner.GetValue());
}

void CCodeGeneratorManager::R50()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	unsigned s = lastTokens.size();
	if (lastTokens[s - 2] != ")")
	{
		m_codeGenerator.MoveToRegister(CheckVar(m_scanner.GetValue()));
	}
	else
	{
		if (lastTokens[s - 4] == "Length")
		{
			m_codeGenerator.MoveToRegister(m_arrs[lastTokens[s - 6]]);
		}
		else if (lastTokens[s - 5] == "GetValue")
		{
			m_codeGenerator.GetValue(lastTokens[s - 7], lastTokens[s - 3]);
		}
		else if (lastTokens[s - 7] == "SetValue")
		{
			m_codeGenerator.SetValue(lastTokens[s - 9], lastTokens[s - 5], lastTokens[s - 3]);
		}
		else
		{
			unsigned i;
			std::vector<std::string> params;
			for (i = s - 1; i >= 0; --i)
			{
				if (lastTokens[i] == ".")
					break;

				if ((lastTokens[i] == ")" && lastTokens[i - 1] != "(") || lastTokens[i] == ",")
					params.push_back(lastTokens[i - 1]);
			}
			std::string objName = lastTokens[i - 1];
			CopyVariables(m_objects[objName], objName);
			m_codeGenerator.MoveToStack(params);
			m_codeGenerator.CallSubProgram(lastTokens[i + 1]);
			
			if (!params.empty())
				m_codeGenerator.ClearStack(IntToString(params.size() * 2));
			
			CopyVariables(m_objects[objName], objName, true);

			m_codeGenerator.MoveToRegister("_operand2");
		}
		
	}
}

void CCodeGeneratorManager::R57()
{
	std::string objName;
	std::string className;
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	for (int i = lastTokens.size() - 1; i >= 0; --i)
	{
		if (lastTokens[i] == "=")
		{
			objName = lastTokens[i - 1];
			className = lastTokens[i + 2];
			m_objects.insert(std::pair<std::string, std::string>(objName, className));
			break;
		}
	}
	CopyVariables(className, objName);
	m_codeGenerator.CallSubProgram(className + "_constructor");
	CopyVariables(className, objName, true);
}

void CCodeGeneratorManager::R58()
{
	std::vector<std::string> & values = m_scanner.GetLastTokens();
	unsigned s = values.size();
	if (values[s - 5] == "Int")
	{
		m_codeGenerator.AddAssignmentCommand(CheckVar(values[s - 8]), CheckVar(values[s - 3]));
	}
	else if (values[s - 5] == "Array")
	{
		m_codeGenerator.AddArrayVariable(values[s - 8], values[s - 3]);
		m_arrs.insert(std::pair<std::string, std::string>(values[s - 8], values[s - 3]));
	}
}

void CCodeGeneratorManager::R59()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	m_operations.push(lastTokens[lastTokens.size() - 2]);
}

void CCodeGeneratorManager::R60()
{
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	m_operations.push(lastTokens[lastTokens.size() - 2]);
}

void CCodeGeneratorManager::ConstructorBegin()
{
	m_codeGenerator.AddSubprogramHeader(m_checker->GetCurrClass() + "_constructor");
}

void CCodeGeneratorManager::ConstructorEnd()
{
	m_codeGenerator.AddSubProgramEnd();
}

void CCodeGeneratorManager::FunctionBegin()
{
	unsigned i;
	std::vector<std::string> & lastTokens = m_scanner.GetLastTokens();
	for (i = lastTokens.size() - 1; i >= 0; --i)
	{
		if (lastTokens[i] == "(")
			break;

		if (lastTokens[i] == "," || (lastTokens[i] == ")" && lastTokens[i - 1] != "("))
			m_params.push_back(lastTokens[i - 1]);
	}
	m_codeGenerator.AddSubprogramHeader(lastTokens[i - 1]);
	m_codeGenerator.PrepareRegisters();
}

void CCodeGeneratorManager::FunctionEnd()
{
	m_codeGenerator.AddSubProgramEnd();
}

void CCodeGeneratorManager::CopyVariables(std::string const & className, std::string const & objName, bool back)
{
	CCharacterTable & ct = m_checker->CharacterTable();
	std::vector<CCharData> vars = ct.GetClass(className).localCharacters;
	for (unsigned i = 0; i < vars.size(); ++i)
	{
		if (back)
		{
			m_codeGenerator.Copy(vars[i].name, objName + "_" + vars[i].name);
		}
		else
		{
			m_codeGenerator.Copy(objName + "_" + vars[i].name, vars[i].name);
		}
	}
}

std::string CCodeGeneratorManager::CheckVar(std::string & v)
{
	for (unsigned i = 0; i < m_params.size(); i++)
	{
		if (m_params[i] == v)
		{
			return "[bp + " + IntToString((m_params.size() + 1 - (i + 1)) * 2) + "]";
		}
	}
	/*if (std::find(m_localVars.begin(), m_localVars.end(), v) != m_localVars.end())
		v = "_l" + v;
	*/
	return v;
}

std::string CCodeGeneratorManager::IntToString(int i)
{
	std::stringstream ss;
	ss << i;
	return ss.str();
}